<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="迎着朝阳的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    Spring核心之基于xml的IOC容器初始化 |  迎着朝阳
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="https://dxysun.com/static/yan.png" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?aa994a8d65700b8835787dd39d079d7e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-springForMainClass"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  Spring核心之基于xml的IOC容器初始化
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2021/08/29/springForMainClass/" class="article-date">
  <time datetime="2021-08-29T14:24:01.000Z" itemprop="datePublished">2021-08-29</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/spring/">spring</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">10.7k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">47 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <p>title: Spring核心之基于xml的IOC容器初始化</p>
<a id="more"></a>

<h1 id="IOC和DI"><a href="#IOC和DI" class="headerlink" title="IOC和DI"></a>IOC和DI</h1><p>IOC（Inversion of Control）控制反转：所谓控制反转，就是把原先我们代码里面需要实现的对象创建、依赖的代码，反转给容器来帮忙实现，那么必然的我们需要创建一个容器，同时需要一种描述来让容器知道需要创建的对象与对象的关系，这个描述最具体表现就是我们所看到的配置文件。</p>
<p>DI（Dependency lnjection）依赖注入：就是指对象是被动接受依赖类而不是自己主动去找，换句话说就是指对象不是从容器中查找它依赖的类，而是在容器实例化对象的时候主动将它依赖的类注入给它。</p>
<h1 id="核心容器类"><a href="#核心容器类" class="headerlink" title="核心容器类"></a>核心容器类</h1><h2 id="BeanFactory"><a href="#BeanFactory" class="headerlink" title="BeanFactory"></a>BeanFactory</h2><p>Spring Bean的创建是典型的工厂模式，这一系列的Bean工厂，也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务，在Spring中有许多的IOC容器的实现供用户选择和使用，其相互关系如下：<br><img src="https://tu.dxysun.com/image-20210829222902975-20210829222904.png" alt="image-20210829222902975"></p>
<p>其中BeanFactory作为最顶层的一个接口类，它定义了IOC容器的基本功能规范，BeanFactory有三个重要的子类：</p>
<p>ListableBeanFactory、 HierarchicalBeanFactory和AutowireCapableBeanFactory。</p>
<p>从类图中可以发现最终的默认实现类是DefaultListableBeanFactory，它实现了所有的接口。</p>
<p>那为何要定义这么多层次的接口呢？</p>
<p>每个接口都有它使用的场合，它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程时，对对象的数据访问所做的限制。</p>
<p>ListableBeanFactory接口表示这些Bean是可列表化的。</p>
<p>HierarchicalBeanFactory表示的是这些Bean是有继承关系的，也就是每个Bean有可能有父Bean。</p>
<p>AutowireCapableBeanFactory接口定义Bean的自动装配规则。</p>
<p>这三个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。</p>
<p>最基本的IOC容器接口BeanFactory，来看一下它的源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//对FactoryBean的转义定义，因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象，</span></span><br><span class="line">	<span class="comment">//如果需要得到工厂本身，需要转义</span></span><br><span class="line">	String FACTORY_BEAN_PREFIX = <span class="string">"&amp;"</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//根据bean的名字，获取在IOC容器中得到bean实例</span></span><br><span class="line">	<span class="function">Object <span class="title">getBean</span><span class="params">(String name)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//根据bean的名字和Class类型来得到bean实例，增加了类型安全验证机制。</span></span><br><span class="line">	&lt;T&gt; <span class="function">T <span class="title">getBean</span><span class="params">(String name, @Nullable Class&lt;T&gt; requiredType)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="function">Object <span class="title">getBean</span><span class="params">(String name, Object... args)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">	&lt;T&gt; <span class="function">T <span class="title">getBean</span><span class="params">(Class&lt;T&gt; requiredType)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">	&lt;T&gt; <span class="function">T <span class="title">getBean</span><span class="params">(Class&lt;T&gt; requiredType, Object... args)</span> <span class="keyword">throws</span> BeansException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//提供对bean的检索，看看是否在IOC容器有这个名字的bean</span></span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">containsBean</span><span class="params">(String name)</span></span>;</span><br><span class="line">  </span><br><span class="line">	<span class="comment">//根据bean名字得到bean实例，并同时判断这个bean是不是单例</span></span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">isSingleton</span><span class="params">(String name)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">isPrototype</span><span class="params">(String name)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line">  </span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">isTypeMatch</span><span class="params">(String name, ResolvableType typeToMatch)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">isTypeMatch</span><span class="params">(String name, @Nullable Class&lt;?&gt; typeToMatch)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//得到bean实例的Class类型</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	Class&lt;?&gt; getType(String name) <span class="keyword">throws</span> NoSuchBeanDefinitionException;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//得到bean的别名，如果根据别名检索，那么其原名也会被检索出来</span></span><br><span class="line">	String[] getAliases(String name);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在BeanFactory里只对IOC容器的基本行为作了定义，根本不关心Bean是如何定义怎样加载的。</p>
<p>正如我们只关心工厂里得到什么的产品对象，至于工厂是怎么生产这些对象的，这个基本的接口不关心。</p>
<p>而要知道工厂是如何产生对象的，我们需要看具体的IOC容器实现，Spring提供了许多IOC容器的实现。</p>
<p>比如GenericApplicationContext、ClasspathXmlApplicationContext等。</p>
<p>ApplicationContext是Spring提供的一个高级的I0C容器，它除了能够提供I0C容器的基本功能外，还为用户提供了以下的附加服务。</p>
<p>从ApplicationContext接口的实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ApplicationContext</span> <span class="keyword">extends</span> <span class="title">EnvironmentCapable</span>, <span class="title">ListableBeanFactory</span>, <span class="title">HierarchicalBeanFactory</span>,</span></span><br><span class="line"><span class="class">		<span class="title">MessageSource</span>, <span class="title">ApplicationEventPublisher</span>, <span class="title">ResourcePatternResolver</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="function">String <span class="title">getId</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function">String <span class="title">getApplicationName</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function">String <span class="title">getDisplayName</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">long</span> <span class="title">getStartupDate</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="function">ApplicationContext <span class="title">getParent</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function">AutowireCapableBeanFactory <span class="title">getAutowireCapableBeanFactory</span><span class="params">()</span> <span class="keyword">throws</span> IllegalStateException</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>看出其特点：</p>
<p>1、支持信息源，可以实现国际化。（实现MessageSource接口）</p>
<p>2、访问资源。（实现ResourcePatternResolver接口）</p>
<p>3、支持应用事件。（实现ApplicationEventPublisher接口）</p>
<h2 id="BeanDefinition"><a href="#BeanDefinition" class="headerlink" title="BeanDefinition"></a>BeanDefinition</h2><p>SpringlOC容器管理了我们定义的各种Bean对象及其相互的关系，Bean对象在Spring实现中是以BeanDefinition来描述的，其继承体系如下：</p>
<p><img src="https://tu.dxysun.com/image-20210829223851850-20210829223852.png" alt="image-20210829223851850"></p>
<h2 id="BeanDefinitionReader"><a href="#BeanDefinitionReader" class="headerlink" title="BeanDefinitionReader"></a>BeanDefinitionReader</h2><p>Bean的解析过程非常复杂，功能被分的很细，因为这里需要被扩展的地方很多，必须保证有足够的灵活性，以应对可能的变化。</p>
<p>Bean的解析主要就是对Spring配置文件的解析，这个解析过程主要通过BeanDefintionReader来完成。</p>
<p>最后看看Spring 中BeanDefintionReader的类结构图：</p>
<p><img src="https://tu.dxysun.com/image-20210829224050821-20210829224052.png" alt="image-20210829224050821"></p>
<h1 id="基于Xml的IOC容器的初始化"><a href="#基于Xml的IOC容器的初始化" class="headerlink" title="基于Xml的IOC容器的初始化"></a>基于Xml的IOC容器的初始化</h1><p>IOC容器的初始化包括BeanDefinition的Resource定位、加载和注册这三个基本的过程。</p>
<p>以ApplicationContext为例讲解，Web项目中使用的XmlWebApplicationContext就属于这个继承体系，还有ClasspathXmlApplicationContext等，其继承体系如下图所示：</p>
<p><img src="https://tu.dxysun.com/image-20210829224503669-20210829224505.png" alt="image-20210829224503669"></p>
<p>ApplicationContext允许上下文嵌套，通过保持父上下文可以维持一个上下文体系。对于Bean的查找可以在这个上下文体系中发生，首先检查当前上下文，其次是父上下文，逐级向上，这样为不同的Spring应用提供了一个共享的Bean定义环境。</p>
<h3 id="入口"><a href="#入口" class="headerlink" title="入口"></a>入口</h3><p>ClassPathXmlApplicationContext，通过main()方法启动：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ApplicationContext app &#x3D; new ClassPathXmlApplicationContext(&quot;application.xml&quot;);</span><br></pre></td></tr></table></figure>

<p>其构造函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public ClassPathXmlApplicationContext(String configLocation) throws BeansException &#123;</span><br><span class="line">	this(new String[] &#123;configLocation&#125;, true, null);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最终调用的构造函数</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ClassPathXmlApplicationContext</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">		String[] configLocations, <span class="keyword">boolean</span> refresh, @Nullable ApplicationContext parent)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">super</span>(parent);</span><br><span class="line">	setConfigLocations(configLocations);</span><br><span class="line">	<span class="keyword">if</span> (refresh) &#123;</span><br><span class="line">		<span class="comment">//重启、刷新、重置</span></span><br><span class="line">		refresh();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>还有像 AnnotationConfigApplicationcontext、FileSystemXmlApplicationContext、XmlWebApplicationContext等都继承自父容器AbstractApplicationContext</p>
<p>主要用到了装饰器模式和策略模式，最终都是调用refresh()方法。</p>
<h3 id="获得配置路径"><a href="#获得配置路径" class="headerlink" title="获得配置路径"></a>获得配置路径</h3><p>通过分析ClassPathXmlApplicationContext的源代码可以知道，在创建ClassPathXmlApplicationContext容器时，构造方法做以下两项</p>
<p>首先，调用父类容器的构造方法<code>super(parent)</code>方法为容器设置好Bean资源加载器。</p>
<p>再调用父类AbstractRefreshableConfigApplicationContext的<code>setConfigLocations(configLocations)</code>方法设置Bean配置信息的定位路径。</p>
<p>通过追踪ClassPathXmlApplicationContext的继承体系，发现其父类的父类AbstractApplicationContext中初始化IOC容器所做的主要源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractApplicationContext</span> <span class="keyword">extends</span> <span class="title">DefaultResourceLoader</span></span></span><br><span class="line"><span class="class">		<span class="keyword">implements</span> <span class="title">ConfigurableApplicationContext</span> </span>&#123;</span><br><span class="line">		...</span><br><span class="line">			<span class="comment">//静态初始化块，在整个容器创建过程中只执行一次</span></span><br><span class="line">	<span class="keyword">static</span> &#123;</span><br><span class="line">		<span class="comment">// Eagerly load the ContextClosedEvent class to avoid weird classloader issues</span></span><br><span class="line">		<span class="comment">// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)</span></span><br><span class="line">		<span class="comment">//为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题，加载IoC容</span></span><br><span class="line">		<span class="comment">//器关闭事件(ContextClosedEvent)类</span></span><br><span class="line">		ContextClosedEvent<span class="class">.<span class="keyword">class</span>.<span class="title">getName</span>()</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	...</span><br><span class="line">		<span class="function"><span class="keyword">public</span> <span class="title">AbstractApplicationContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.resourcePatternResolver = getResourcePatternResolver();</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">		<span class="comment">//获取一个Spring Source的加载器用于读入Spring Bean定义资源文件</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> ResourcePatternResolver <span class="title">getResourcePatternResolver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">//AbstractApplicationContext继承DefaultResourceLoader，因此也是一个资源加载器</span></span><br><span class="line">		<span class="comment">//Spring资源加载器，其getResource(String location)方法用于载入资源</span></span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> PathMatchingResourcePatternResolver(<span class="keyword">this</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>AbstractApplicationContext 的默认构造方法中有调用PathMatchingResourcePatternResolver的构造方法创建Sprina资源加载器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">PathMatchingResourcePatternResolver</span><span class="params">(ResourceLoader resourceLoader)</span> </span>&#123;</span><br><span class="line">	Assert.notNull(resourceLoader, <span class="string">"ResourceLoader must not be null"</span>);</span><br><span class="line">	<span class="comment">//设置Spring的资源加载器</span></span><br><span class="line">	<span class="keyword">this</span>.resourceLoader = resourceLoader;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在设置容器的资源加载器之后，接下来ClassPathXmlAplicationContext执行<code>setConfigLocations()</code>方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean配置信息的定位，该方法的源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//处理单个资源文件路径为一个字符串的情况</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setConfigLocation</span><span class="params">(String location)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";</span></span><br><span class="line">	<span class="comment">//即多个资源文件路径之间用” ,; \t\n”分隔，解析成数组形式</span></span><br><span class="line">	setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));</span><br><span class="line">&#125;</span><br><span class="line"> <span class="comment">//解析Bean定义资源文件的路径，处理多个资源文件字符串数组</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setConfigLocations</span><span class="params">(@Nullable String... locations)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (locations != <span class="keyword">null</span>) &#123;</span><br><span class="line">		Assert.noNullElements(locations, <span class="string">"Config locations must not be null"</span>);</span><br><span class="line">		<span class="keyword">this</span>.configLocations = <span class="keyword">new</span> String[locations.length];</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; locations.length; i++) &#123;</span><br><span class="line">			<span class="comment">// resolvePath为同一个类中将字符串解析为路径的方法</span></span><br><span class="line">			<span class="keyword">this</span>.configLocations[i] = resolvePath(locations[i]).trim();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		<span class="keyword">this</span>.configLocations = <span class="keyword">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过这两个方法的源码我们可以看出，我们既可以使用一个字符串来配置多个Spring Bean配置信息</p>
<p>也可以使用字符串数组，即下面两种方式都是可以的，多个资源文件路径之间可以是用<code>&quot;,; \t\n&quot;</code>等分隔。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ClassPathResource res = <span class="keyword">new</span> ClassPathResource(<span class="string">"a.xml,b.xml"</span>);</span><br><span class="line">ClassPathResource res = <span class="keyword">new</span> ClassPathResource(<span class="keyword">new</span> String[]&#123;<span class="string">"a.xml"</span>,<span class="string">"b.xml"</span>&#125;);</span><br></pre></td></tr></table></figure>

<p>至此，SpringlOC容器在初始化时将配置的Bean配置信息定位为Spring封装的Resource。</p>
<h3 id="开始启动"><a href="#开始启动" class="headerlink" title="开始启动"></a>开始启动</h3><p>SpringlOC容器对Bean配置资源的载入是从<code>refresh()</code>函数开始的，<code>refresh()</code>是一个模板方法，规定了IOC容器的启动流程，有些逻辑要子类去实现，它对Bean配置资源进行载入。</p>
<p>ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的<code>refresh()</code>函数启动整个IOC容器对Bean定义的载入过程，现在来详细看看refresh()中的逻辑处理：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">refresh</span><span class="params">()</span> <span class="keyword">throws</span> BeansException, IllegalStateException </span>&#123;</span><br><span class="line">	<span class="keyword">synchronized</span> (<span class="keyword">this</span>.startupShutdownMonitor) &#123;</span><br><span class="line">		<span class="comment">// Prepare this context for refreshing.</span></span><br><span class="line">		<span class="comment">//1、调用容器准备刷新的方法，获取容器的当时时间，同时给容器设置同步标识</span></span><br><span class="line">		prepareRefresh();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Tell the subclass to refresh the internal bean factory.</span></span><br><span class="line">		<span class="comment">//2、告诉子类启动refreshBeanFactory()方法，Bean定义资源文件的载入从</span></span><br><span class="line">		<span class="comment">//子类的refreshBeanFactory()方法启动</span></span><br><span class="line">		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Prepare the bean factory for use in this context.</span></span><br><span class="line">		<span class="comment">//3、为BeanFactory配置容器特性，例如类加载器、事件处理器等</span></span><br><span class="line">		prepareBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">// Allows post-processing of the bean factory in context subclasses.</span></span><br><span class="line">			<span class="comment">//4、为容器的某些子类指定特殊的BeanPost事件处理器</span></span><br><span class="line">			postProcessBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Invoke factory processors registered as beans in the context.</span></span><br><span class="line">			<span class="comment">//5、调用所有注册的BeanFactoryPostProcessor的Bean</span></span><br><span class="line">			invokeBeanFactoryPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Register bean processors that intercept bean creation.</span></span><br><span class="line">			<span class="comment">//6、为BeanFactory注册BeanPost事件处理器.</span></span><br><span class="line">			<span class="comment">//BeanPostProcessor是Bean后置处理器，用于监听容器触发的事件</span></span><br><span class="line">			registerBeanPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize message source for this context.</span></span><br><span class="line">			<span class="comment">//7、初始化信息源，和国际化相关.</span></span><br><span class="line">			initMessageSource();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize event multicaster for this context.</span></span><br><span class="line">			<span class="comment">//8、初始化容器事件传播器.</span></span><br><span class="line">			initApplicationEventMulticaster();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize other special beans in specific context subclasses.</span></span><br><span class="line">			<span class="comment">//9、调用子类的某些特殊Bean初始化方法</span></span><br><span class="line">			onRefresh();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Check for listener beans and register them.</span></span><br><span class="line">			<span class="comment">//10、为事件传播器注册事件监听器.</span></span><br><span class="line">			registerListeners();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Instantiate all remaining (non-lazy-init) singletons.</span></span><br><span class="line">			<span class="comment">//11、初始化所有剩余的单例Bean</span></span><br><span class="line">			finishBeanFactoryInitialization(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Last step: publish corresponding event.</span></span><br><span class="line">			<span class="comment">//12、初始化容器的生命周期事件处理器，并发布容器的生命周期事件</span></span><br><span class="line">			finishRefresh();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">			<span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">				logger.warn(<span class="string">"Exception encountered during context initialization - "</span> +</span><br><span class="line">						<span class="string">"cancelling refresh attempt: "</span> + ex);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Destroy already created singletons to avoid dangling resources.</span></span><br><span class="line">			<span class="comment">//13、销毁已创建的Bean</span></span><br><span class="line">			destroyBeans();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Reset 'active' flag.</span></span><br><span class="line">			<span class="comment">//14、取消refresh操作，重置容器的同步标识。</span></span><br><span class="line">			cancelRefresh(ex);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Propagate exception to caller.</span></span><br><span class="line">			<span class="keyword">throw</span> ex;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			<span class="comment">// Reset common introspection caches in Spring's core, since we</span></span><br><span class="line">			<span class="comment">// might not ever need metadata for singleton beans anymore...</span></span><br><span class="line">			<span class="comment">//15、重设公共缓存</span></span><br><span class="line">			resetCommonCaches();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>refresh()</code>方法主要为IOC容器Bean的生命周期管理提供条件，SpringIOC容器载入Bean配置信息从其子类容器的<code>refreshBeanFactory()</code>方法启动，所以整个<code>refresh()</code>中<code>ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();</code>这句以后代码的都是注册容器的信息源和生命周期事件，前面说的载入就是从这句代码开始启动。</p>
<p><code>refresh()</code>方法的主要作用是：</p>
<p>在创建IOC容器前，如果已经有容器存在，则需要把已有的容器销毁和关闭，以保证在refresh之后使用的是新建立起来的I0C容器。它类似于对IOC容器的重启，在新建立好的容器中对容器进行初始化，对Bean配置资源进行载入。</p>
<h3 id="创建容器"><a href="#创建容器" class="headerlink" title="创建容器"></a>创建容器</h3><p><code>obtainFreshBeanFactory()</code>方法调用子类容器的<code>refreshBeanFactory()</code>方法，启动容器载入Bean配置信息的过程，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ConfigurableListableBeanFactory <span class="title">obtainFreshBeanFactory</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	<span class="comment">//这里使用了委派设计模式，父类定义了抽象的refreshBeanFactory()方法，具体实现调用子类容器的refreshBeanFactory()方法</span></span><br><span class="line">	refreshBeanFactory();</span><br><span class="line">	ConfigurableListableBeanFactory beanFactory = getBeanFactory();</span><br><span class="line">	<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">		logger.debug(<span class="string">"Bean factory for "</span> + getDisplayName() + <span class="string">": "</span> + beanFactory);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> beanFactory;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>AbstractApplicationcontext类中只抽象定义了<code>refreshBeanFactory()</code>方法，容器真正调用的是其子AbstractRefreshableApplicationcontext实现的<code>refreshBeanFactory()</code>方法，方法的源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">refreshBeanFactory</span><span class="params">()</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">	<span class="comment">//如果已经有容器，销毁容器中的bean，关闭容器</span></span><br><span class="line">	<span class="keyword">if</span> (hasBeanFactory()) &#123;</span><br><span class="line">		destroyBeans();</span><br><span class="line">		closeBeanFactory();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		<span class="comment">//创建IOC容器</span></span><br><span class="line">		DefaultListableBeanFactory beanFactory = createBeanFactory();</span><br><span class="line">		beanFactory.setSerializationId(getId());</span><br><span class="line">		<span class="comment">//对IOC容器进行定制化，如设置启动参数，开启注解的自动装配等</span></span><br><span class="line">		customizeBeanFactory(beanFactory);</span><br><span class="line">		<span class="comment">//调用载入Bean定义的方法，主要这里又使用了一个委派模式，在当前类中只定义了抽象的loadBeanDefinitions方法，具体的实现调用子类容器</span></span><br><span class="line">		loadBeanDefinitions(beanFactory);</span><br><span class="line">		<span class="keyword">synchronized</span> (<span class="keyword">this</span>.beanFactoryMonitor) &#123;</span><br><span class="line">			<span class="keyword">this</span>.beanFactory = beanFactory;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> ApplicationContextException(<span class="string">"I/O error parsing bean definition source for "</span> + getDisplayName(), ex);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在这个方法中，先判断BeanFactory是否存在，如果存在则先销毁beans并关闭beanFactory，接着创建DefaultListableBeanFactory，并调用<code>loadBeanDefinitions(beanFactory)</code>装载bean定义。</p>
<h3 id="载入配置路径"><a href="#载入配置路径" class="headerlink" title="载入配置路径"></a>载入配置路径</h3><p>AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法，容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实现父类抽象的载入Bean定义方法</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">loadBeanDefinitions</span><span class="params">(DefaultListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException, IOException </span>&#123;</span><br><span class="line">	<span class="comment">// Create a new XmlBeanDefinitionReader for the given BeanFactory.</span></span><br><span class="line">	<span class="comment">//创建XmlBeanDefinitionReader，即创建Bean读取器，并通过回调设置到容器中去，容器使用该读取器读取Bean定义资源</span></span><br><span class="line">	XmlBeanDefinitionReader beanDefinitionReader = <span class="keyword">new</span> XmlBeanDefinitionReader(beanFactory);</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Configure the bean definition reader with this context's</span></span><br><span class="line">	<span class="comment">// resource loading environment.</span></span><br><span class="line">	<span class="comment">//为Bean读取器设置Spring资源加载器，AbstractXmlApplicationContext的</span></span><br><span class="line">	<span class="comment">//祖先父类AbstractApplicationContext继承DefaultResourceLoader，因此，容器本身也是一个资源加载器</span></span><br><span class="line">	beanDefinitionReader.setEnvironment(<span class="keyword">this</span>.getEnvironment());</span><br><span class="line">	beanDefinitionReader.setResourceLoader(<span class="keyword">this</span>);</span><br><span class="line">	<span class="comment">//为Bean读取器设置SAX xml解析器</span></span><br><span class="line">	beanDefinitionReader.setEntityResolver(<span class="keyword">new</span> ResourceEntityResolver(<span class="keyword">this</span>));</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Allow a subclass to provide custom initialization of the reader,</span></span><br><span class="line">	<span class="comment">// then proceed with actually loading the bean definitions.</span></span><br><span class="line">	<span class="comment">//当Bean读取器读取Bean定义的Xml资源文件时，启用Xml的校验机制</span></span><br><span class="line">	initBeanDefinitionReader(beanDefinitionReader);</span><br><span class="line">	<span class="comment">//Bean读取器真正实现加载的方法</span></span><br><span class="line">	loadBeanDefinitions(beanDefinitionReader);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>AbstractXmlApplicationContext的<code>loadBeanDefinitions(beanDefinitionReader)</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Xml Bean读取器加载Bean定义资源</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">loadBeanDefinitions</span><span class="params">(XmlBeanDefinitionReader reader)</span> <span class="keyword">throws</span> BeansException, IOException </span>&#123;</span><br><span class="line">	<span class="comment">//获取Bean定义资源的定位</span></span><br><span class="line">	Resource[] configResources = getConfigResources();</span><br><span class="line">	<span class="keyword">if</span> (configResources != <span class="keyword">null</span>) &#123;</span><br><span class="line">		<span class="comment">//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位</span></span><br><span class="line">		<span class="comment">//的Bean定义资源</span></span><br><span class="line">		reader.loadBeanDefinitions(configResources);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子类中获取的Bean定义资源定位为空，则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源</span></span><br><span class="line">	String[] configLocations = getConfigLocations();</span><br><span class="line">	<span class="keyword">if</span> (configLocations != <span class="keyword">null</span>) &#123;</span><br><span class="line">		<span class="comment">//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位</span></span><br><span class="line">		<span class="comment">//的Bean定义资源</span></span><br><span class="line">		reader.loadBeanDefinitions(configLocations);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以XmlBean读取器的其中一种策略XmlBeanDefinitionReader为例。</p>
<p>XmlBeanDefinitionReader调用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions()方法读取Bean配置资源。</p>
<p>由于我们使用ClassPathXmlApplicationContext作为例子分析，因此getConfigResources的返回值为null，因此程序执行<code>reader.loadBeanDefinitions(configLocations)</code>分支。</p>
<h3 id="分配路径处理策略"><a href="#分配路径处理策略" class="headerlink" title="分配路径处理策略"></a>分配路径处理策略</h3><p>在XmlBeanDefinitionReader的抽象父类AbstractBeanDefinitionReader中定义了载入过程。</p>
<p>AbstractBeanDefinitionReader的<code>loadBeanDefinitions(String...)</code>方法源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">	<span class="comment">//重载方法，调用loadBeanDefinitions(String);</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">loadBeanDefinitions</span><span class="params">(String... locations)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		Assert.notNull(locations, <span class="string">"Location array must not be null"</span>);</span><br><span class="line">		<span class="keyword">int</span> counter = <span class="number">0</span>;</span><br><span class="line">		<span class="keyword">for</span> (String location : locations) &#123;</span><br><span class="line">			counter += loadBeanDefinitions(location);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> counter;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="comment">//重载方法，调用下面的loadBeanDefinitions(String, Set&lt;Resource&gt;);方法</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">loadBeanDefinitions</span><span class="params">(String location)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> loadBeanDefinitions(location, <span class="keyword">null</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">loadBeanDefinitions</span><span class="params">(String location, @Nullable Set&lt;Resource&gt; actualResources)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		<span class="comment">//获取在IoC容器初始化过程中设置的资源加载器</span></span><br><span class="line">		ResourceLoader resourceLoader = getResourceLoader();</span><br><span class="line">		<span class="keyword">if</span> (resourceLoader == <span class="keyword">null</span>) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(</span><br><span class="line">					<span class="string">"Cannot import bean definitions from location ["</span> + location + <span class="string">"]: no ResourceLoader available"</span>);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (resourceLoader <span class="keyword">instanceof</span> ResourcePatternResolver) &#123;</span><br><span class="line">			<span class="comment">// Resource pattern matching available.</span></span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				<span class="comment">//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源</span></span><br><span class="line">				<span class="comment">//加载多个指定位置的Bean定义资源文件</span></span><br><span class="line">				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);</span><br><span class="line">				<span class="comment">//委派调用其子类XmlBeanDefinitionReader的方法，实现加载功能</span></span><br><span class="line">				<span class="keyword">int</span> loadCount = loadBeanDefinitions(resources);</span><br><span class="line">				<span class="keyword">if</span> (actualResources != <span class="keyword">null</span>) &#123;</span><br><span class="line">					<span class="keyword">for</span> (Resource resource : resources) &#123;</span><br><span class="line">						actualResources.add(resource);</span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">					logger.debug(<span class="string">"Loaded "</span> + loadCount + <span class="string">" bean definitions from location pattern ["</span> + location + <span class="string">"]"</span>);</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">return</span> loadCount;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(</span><br><span class="line">						<span class="string">"Could not resolve bean definition resource pattern ["</span> + location + <span class="string">"]"</span>, ex);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="comment">// Can only load single resources by absolute URL.</span></span><br><span class="line">			<span class="comment">//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源</span></span><br><span class="line">			<span class="comment">//加载单个指定位置的Bean定义资源文件</span></span><br><span class="line">			Resource resource = resourceLoader.getResource(location);</span><br><span class="line">			<span class="comment">//委派调用其子类XmlBeanDefinitionReader的方法，实现加载功能</span></span><br><span class="line">			<span class="keyword">int</span> loadCount = loadBeanDefinitions(resource);</span><br><span class="line">			<span class="keyword">if</span> (actualResources != <span class="keyword">null</span>) &#123;</span><br><span class="line">				actualResources.add(resource);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">				logger.debug(<span class="string">"Loaded "</span> + loadCount + <span class="string">" bean definitions from location ["</span> + location + <span class="string">"]"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span> loadCount;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>AbstractRefreshableConfigApplicationContext 的<code>loadBeanDefinitions(Resource..resources)</code>方法实际上是调用AbstractBeanDefinitionReader的<code>loadBeanDefinitions()</code>方法。</p>
<p>从对AbstractBeanDefinitionReader 的<code>loadBeanDefinitions()</code>方法源码分析可以看出该方法就做了两件事：</p>
<p>首先，调用资源加载器的获取资源方法<code>resourceloader.getResource(location)</code>，获取到要加载的资源。</p>
<p>其次，真正执行加载功能是其子类XmlBeanDefinitionReader的<code>loadBeanDefinitions()</code>方法。</p>
<p>在名 <code>loadBeanDefinitions()</code>方法中调用了<code>AbstractApplicationContext的getResources()</code>方法，跟进去之后发现<code>getResources()</code>方法其实定义在ResourcePatternResolver中，此时，有必要来看一下ResourcePatternResolver的全类图：</p>
<p><img src="https://tu.dxysun.com/image-20210829233721037-20210829233722.png" alt="image-20210829233721037"></p>
<p>从上面可以看到ResourceLoader与ApplicationContext的继承关系，可以看出其实际调用的是DefaultResourceLoader中的<code>getSource()</code>方法定位Resource因为ClassPathXmlApplicationContext本身就是DefaultResourceloader的实现类，所以此时又回到了ClassPathXmlApplicationContext中来。</p>
<h3 id="解析配置文件路径"><a href="#解析配置文件路径" class="headerlink" title="解析配置文件路径"></a>解析配置文件路径</h3><p>XmlBeanDefinitionReader通过调用 ClassPathXmlApplicationContext的父类DefaultResourceLoader的getResource()方法获取要加载的资源，其源码如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取Resource的具体实现方法</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Resource <span class="title">getResource</span><span class="params">(String location)</span> </span>&#123;</span><br><span class="line">	Assert.notNull(location, <span class="string">"Location must not be null"</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (ProtocolResolver protocolResolver : <span class="keyword">this</span>.protocolResolvers) &#123;</span><br><span class="line">		Resource resource = protocolResolver.resolve(location, <span class="keyword">this</span>);</span><br><span class="line">		<span class="keyword">if</span> (resource != <span class="keyword">null</span>) &#123;</span><br><span class="line">			<span class="keyword">return</span> resource;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果是类路径的方式，那需要使用ClassPathResource 来得到bean 文件的资源对象</span></span><br><span class="line">	<span class="keyword">if</span> (location.startsWith(<span class="string">"/"</span>)) &#123;</span><br><span class="line">		<span class="keyword">return</span> getResourceByPath(location);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (location.startsWith(CLASSPATH_URL_PREFIX)) &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">// Try to parse the location as a URL...</span></span><br><span class="line">			<span class="comment">// 如果是URL 方式，使用UrlResource 作为bean 文件的资源对象</span></span><br><span class="line">			URL url = <span class="keyword">new</span> URL(location);</span><br><span class="line">			<span class="keyword">return</span> (ResourceUtils.isFileURL(url) ? <span class="keyword">new</span> FileUrlResource(url) : <span class="keyword">new</span> UrlResource(url));</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (MalformedURLException ex) &#123;</span><br><span class="line">			<span class="comment">// No URL -&gt; resolve as resource path.</span></span><br><span class="line">			<span class="comment">//如果既不是classpath标识，又不是URL标识的Resource定位，则调用</span></span><br><span class="line">			<span class="comment">//容器本身的getResourceByPath方法获取Resource</span></span><br><span class="line">			<span class="keyword">return</span> getResourceByPath(location);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>DefaultResourceloader提供了<code>getResourceByPath()</code>方法的实现，就是为了处理既不是classpath 标识，又不是URL标识的Resource定位这种情况</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> Resource <span class="title">getResourceByPath</span><span class="params">(String path)</span></span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> ClassPathContextResource(path, getclassloader());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在ClassPathResource中完成了对整个路径的解析。</p>
<p>这样，就可以从类路径上对IOC配置文件进行加载，当然我们可以按照这个逻辑从任何地方加载，在Spring中我们看到它提供的各种资源抽象，比如ClassPathResource、 URLResource、FileSystemResource等来供我们使用。</p>
<p>上面我们看到的是定位 Resource的一个过程，FileSystemXmlApplication容器就重写了<code>getResourceByPath()</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> Resource <span class="title">getResourceByPath</span><span class="params">(String path)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (path.startsWith(<span class="string">"/"</span>)) &#123;</span><br><span class="line">		path = path.substring(<span class="number">1</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//这里使用文件系统资源对象来定义bean 文件</span></span><br><span class="line">	<span class="keyword">return</span> <span class="keyword">new</span> FileSystemResource(path);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过子类的覆盖，巧妙地完成了将类路径变为文件路径的转换。</p>
<h3 id="开始读取配置内容"><a href="#开始读取配置内容" class="headerlink" title="开始读取配置内容"></a>开始读取配置内容</h3><p>继续回到XmlBeanDefinitionReader 的<code>loadBeanDefinitions(Resoure...)</code>方法看到代表bean文件的资源定义以后的载入过程。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line">	<span class="comment">//XmlBeanDefinitionReader加载资源的入口方法</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">loadBeanDefinitions</span><span class="params">(Resource resource)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		<span class="comment">//将读入的XML资源进行特殊编码处理</span></span><br><span class="line">		<span class="keyword">return</span> loadBeanDefinitions(<span class="keyword">new</span> EncodedResource(resource));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//这里是载入XML形式Bean定义资源文件方法</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">loadBeanDefinitions</span><span class="params">(EncodedResource encodedResource)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		Assert.notNull(encodedResource, <span class="string">"EncodedResource must not be null"</span>);</span><br><span class="line">		<span class="keyword">if</span> (logger.isInfoEnabled()) &#123;</span><br><span class="line">			logger.info(<span class="string">"Loading XML bean definitions from "</span> + encodedResource.getResource());</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		Set&lt;EncodedResource&gt; currentResources = <span class="keyword">this</span>.resourcesCurrentlyBeingLoaded.get();</span><br><span class="line">		<span class="keyword">if</span> (currentResources == <span class="keyword">null</span>) &#123;</span><br><span class="line">			currentResources = <span class="keyword">new</span> HashSet&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">			<span class="keyword">this</span>.resourcesCurrentlyBeingLoaded.set(currentResources);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (!currentResources.add(encodedResource)) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(</span><br><span class="line">					<span class="string">"Detected cyclic loading of "</span> + encodedResource + <span class="string">" - check your import definitions!"</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">//将资源文件转为InputStream的IO流</span></span><br><span class="line">			InputStream inputStream = encodedResource.getResource().getInputStream();</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				<span class="comment">//从InputStream中得到XML的解析源</span></span><br><span class="line">				InputSource inputSource = <span class="keyword">new</span> InputSource(inputStream);</span><br><span class="line">				<span class="keyword">if</span> (encodedResource.getEncoding() != <span class="keyword">null</span>) &#123;</span><br><span class="line">					inputSource.setEncoding(encodedResource.getEncoding());</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="comment">//这里是具体的读取过程</span></span><br><span class="line">				<span class="keyword">return</span> doLoadBeanDefinitions(inputSource, encodedResource.getResource());</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">finally</span> &#123;</span><br><span class="line">				<span class="comment">//关闭从Resource中得到的IO流</span></span><br><span class="line">				inputStream.close();</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(</span><br><span class="line">					<span class="string">"IOException parsing XML document from "</span> + encodedResource.getResource(), ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			currentResources.remove(encodedResource);</span><br><span class="line">			<span class="keyword">if</span> (currentResources.isEmpty()) &#123;</span><br><span class="line">				<span class="keyword">this</span>.resourcesCurrentlyBeingLoaded.remove();</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//从特定XML文件中实际载入Bean定义资源的方法</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">doLoadBeanDefinitions</span><span class="params">(InputSource inputSource, Resource resource)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">//将XML文件转换为DOM对象，解析过程由documentLoader实现</span></span><br><span class="line">			Document doc = doLoadDocument(inputSource, resource);</span><br><span class="line">			<span class="comment">//这里是启动对Bean定义解析的详细过程，该解析过程会用到Spring的Bean配置规则</span></span><br><span class="line">			<span class="keyword">return</span> registerBeanDefinitions(doc, resource);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (BeanDefinitionStoreException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> ex;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (SAXParseException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> XmlBeanDefinitionStoreException(resource.getDescription(),</span><br><span class="line">					<span class="string">"Line "</span> + ex.getLineNumber() + <span class="string">" in XML document from "</span> + resource + <span class="string">" is invalid"</span>, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (SAXException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> XmlBeanDefinitionStoreException(resource.getDescription(),</span><br><span class="line">					<span class="string">"XML document from "</span> + resource + <span class="string">" is invalid"</span>, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (ParserConfigurationException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(resource.getDescription(),</span><br><span class="line">					<span class="string">"Parser configuration exception parsing XML from "</span> + resource, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(resource.getDescription(),</span><br><span class="line">					<span class="string">"IOException parsing XML document from "</span> + resource, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(resource.getDescription(),</span><br><span class="line">					<span class="string">"Unexpected exception parsing XML document from "</span> + resource, ex);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> Document <span class="title">doLoadDocument</span><span class="params">(InputSource inputSource, Resource resource)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">this</span>.documentLoader.loadDocument(inputSource, getEntityResolver(), <span class="keyword">this</span>.errorHandler,</span><br><span class="line">				getValidationModeForResource(resource), isNamespaceAware());</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>通过源码分析，载入Bean配置信息的最后一步是将Bean配置信息转换为Document对象，该过程由documentLoader()方法实现。</p>
<h3 id="准备文档对象"><a href="#准备文档对象" class="headerlink" title="准备文档对象"></a>准备文档对象</h3><p>DocumentLoader将Bean配置资源转换成Document对象的源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//使用标准的JAXP将载入的Bean定义资源转换成document对象</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Document <span class="title">loadDocument</span><span class="params">(InputSource inputSource, EntityResolver entityResolver,</span></span></span><br><span class="line"><span class="function"><span class="params">		ErrorHandler errorHandler, <span class="keyword">int</span> validationMode, <span class="keyword">boolean</span> namespaceAware)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//创建文件解析器工厂</span></span><br><span class="line">	DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);</span><br><span class="line">	<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">		logger.debug(<span class="string">"Using JAXP provider ["</span> + factory.getClass().getName() + <span class="string">"]"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//创建文档解析器</span></span><br><span class="line">	DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);</span><br><span class="line">	<span class="comment">//解析Spring的Bean定义资源</span></span><br><span class="line">	<span class="keyword">return</span> builder.parse(inputSource);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> DocumentBuilderFactory <span class="title">createDocumentBuilderFactory</span><span class="params">(<span class="keyword">int</span> validationMode, <span class="keyword">boolean</span> namespaceAware)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> ParserConfigurationException </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//创建文档解析工厂</span></span><br><span class="line">	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();</span><br><span class="line">	factory.setNamespaceAware(namespaceAware);</span><br><span class="line"></span><br><span class="line">	<span class="comment">//设置解析XML的校验</span></span><br><span class="line">	<span class="keyword">if</span> (validationMode != XmlValidationModeDetector.VALIDATION_NONE) &#123;</span><br><span class="line">		factory.setValidating(<span class="keyword">true</span>);</span><br><span class="line">		<span class="keyword">if</span> (validationMode == XmlValidationModeDetector.VALIDATION_XSD) &#123;</span><br><span class="line">			<span class="comment">// Enforce namespace aware for XSD...</span></span><br><span class="line">			factory.setNamespaceAware(<span class="keyword">true</span>);</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (IllegalArgumentException ex) &#123;</span><br><span class="line">				ParserConfigurationException pcex = <span class="keyword">new</span> ParserConfigurationException(</span><br><span class="line">						<span class="string">"Unable to validate using XSD: Your JAXP provider ["</span> + factory +</span><br><span class="line">						<span class="string">"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? "</span> +</span><br><span class="line">						<span class="string">"Upgrade to Apache Xerces (or Java 1.5) for full XSD support."</span>);</span><br><span class="line">				pcex.initCause(ex);</span><br><span class="line">				<span class="keyword">throw</span> pcex;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> factory;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的解析过程是调用JavaEE标准的JAXP标准进行处理。</p>
<p>至此SpringIOC容器根据定位的Bean配置信息，将其加载读入并转换成为Document对象过程完成。</p>
<p>接下来我们要继续分析SpringIOC容器将载入的Bean配置信息转换为Document对象之后，是如何将其解析为SpringIoc管理的Bean对象并将其注册到容器中的。</p>
<h3 id="分配解析策略"><a href="#分配解析策略" class="headerlink" title="分配解析策略"></a>分配解析策略</h3><p>XmlBeanDefinitionReader类中的<code>doloadBeanDefinition()</code>方法是从特定XML文件中实际载入Bean配置资源的方法，该方法在载入Bean配置资源之后将其转换为Document对象，接下来调用<code>registerBeanDefinitions()</code>启动SpringIOC容器对Bean定义的解析过程，<code>registerBeanDefinitions()</code>方法源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">registerBeanDefinitions</span><span class="params">(Document doc, Resource resource)</span> <span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line">	<span class="comment">//得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析</span></span><br><span class="line">	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();</span><br><span class="line">	<span class="comment">//获得容器中注册的Bean数量</span></span><br><span class="line">	<span class="keyword">int</span> countBefore = getRegistry().getBeanDefinitionCount();</span><br><span class="line">	<span class="comment">//解析过程入口，这里使用了委派模式，BeanDefinitionDocumentReader只是个接口,</span></span><br><span class="line">	<span class="comment">//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成</span></span><br><span class="line">	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));</span><br><span class="line">	<span class="comment">//统计解析的Bean数量</span></span><br><span class="line">	<span class="keyword">return</span> getRegistry().getBeanDefinitionCount() - countBefore;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Bean配置资源的载入解析分为以下两个过程：</p>
<p>首先，通过调用XML解析器将Bean配置信息转换得到Document对象，但是这些Document对象并没有按照Spring的Bean规则进行解析。这一步是载入的过程</p>
<p>其次，在完成通用的XML解析之后，按照Spring Bean的定义规则对Document对象进行解析，其解析过程是在接口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中实现。</p>
<h3 id="将配置载入内存"><a href="#将配置载入内存" class="headerlink" title="将配置载入内存"></a>将配置载入内存</h3><p>BeanDefinitionDocumentReader接口通过<code>registerBeanDefinitions()</code>方法调用其实现类DefaultBeanDefinitionDocumentReader对Document对象进行解析，解析的代码如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//根据Spring DTD对Bean的定义规则解析Bean定义Document对象</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinitions</span><span class="params">(Document doc, XmlReaderContext readerContext)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//获得XML描述符</span></span><br><span class="line">	<span class="keyword">this</span>.readerContext = readerContext;</span><br><span class="line">	logger.debug(<span class="string">"Loading bean definitions"</span>);</span><br><span class="line">	<span class="comment">//获得Document的根元素</span></span><br><span class="line">	Element root = doc.getDocumentElement();</span><br><span class="line">	doRegisterBeanDefinitions(root);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doRegisterBeanDefinitions</span><span class="params">(Element root)</span> </span>&#123;</span><br><span class="line">	<span class="comment">// Any nested &lt;beans&gt; elements will cause recursion in this method. In</span></span><br><span class="line">	<span class="comment">// order to propagate and preserve &lt;beans&gt; default-* attributes correctly,</span></span><br><span class="line">	<span class="comment">// keep track of the current (parent) delegate, which may be null. Create</span></span><br><span class="line">	<span class="comment">// the new (child) delegate with a reference to the parent for fallback purposes,</span></span><br><span class="line">	<span class="comment">// then ultimately reset this.delegate back to its original (parent) reference.</span></span><br><span class="line">	<span class="comment">// this behavior emulates a stack of delegates without actually necessitating one.</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">//具体的解析过程由BeanDefinitionParserDelegate实现，</span></span><br><span class="line">	<span class="comment">//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素</span></span><br><span class="line">	BeanDefinitionParserDelegate parent = <span class="keyword">this</span>.delegate;</span><br><span class="line">	<span class="keyword">this</span>.delegate = createDelegate(getReaderContext(), root, parent);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (<span class="keyword">this</span>.delegate.isDefaultNamespace(root)) &#123;</span><br><span class="line">		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);</span><br><span class="line">		<span class="keyword">if</span> (StringUtils.hasText(profileSpec)) &#123;</span><br><span class="line">			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(</span><br><span class="line">					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);</span><br><span class="line">			<span class="keyword">if</span> (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) &#123;</span><br><span class="line">				<span class="keyword">if</span> (logger.isInfoEnabled()) &#123;</span><br><span class="line">					logger.info(<span class="string">"Skipped XML bean definition file due to specified profiles ["</span> + profileSpec +</span><br><span class="line">							<span class="string">"] not matching: "</span> + getReaderContext().getResource());</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">return</span>;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//在解析Bean定义之前，进行自定义的解析，增强解析过程的可扩展性</span></span><br><span class="line">	preProcessXml(root);</span><br><span class="line">	<span class="comment">//从Document的根元素开始进行Bean定义的Document对象</span></span><br><span class="line">	parseBeanDefinitions(root, <span class="keyword">this</span>.delegate);</span><br><span class="line">	<span class="comment">//在解析Bean定义之后，进行自定义的解析，增加解析过程的可扩展性</span></span><br><span class="line">	postProcessXml(root);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">this</span>.delegate = parent;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//创建BeanDefinitionParserDelegate，用于完成真正的解析过程</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> BeanDefinitionParserDelegate <span class="title">createDelegate</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">		XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	BeanDefinitionParserDelegate delegate = <span class="keyword">new</span> BeanDefinitionParserDelegate(readerContext);</span><br><span class="line">	<span class="comment">//BeanDefinitionParserDelegate初始化Document根元素</span></span><br><span class="line">	delegate.initDefaults(root, parentDelegate);</span><br><span class="line">	<span class="keyword">return</span> delegate;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">parseBeanDefinitions</span><span class="params">(Element root, BeanDefinitionParserDelegate delegate)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//Bean定义的Document对象使用了Spring默认的XML命名空间</span></span><br><span class="line">	<span class="keyword">if</span> (delegate.isDefaultNamespace(root)) &#123;</span><br><span class="line">		<span class="comment">//获取Bean定义的Document对象根元素的所有子节点</span></span><br><span class="line">		NodeList nl = root.getChildNodes();</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nl.getLength(); i++) &#123;</span><br><span class="line">			Node node = nl.item(i);</span><br><span class="line">			<span class="comment">//获得Document节点是XML元素节点</span></span><br><span class="line">			<span class="keyword">if</span> (node <span class="keyword">instanceof</span> Element) &#123;</span><br><span class="line">				Element ele = (Element) node;</span><br><span class="line">				<span class="comment">//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间</span></span><br><span class="line">				<span class="keyword">if</span> (delegate.isDefaultNamespace(ele)) &#123;</span><br><span class="line">					<span class="comment">//使用Spring的Bean规则解析元素节点</span></span><br><span class="line">					parseDefaultElement(ele, delegate);</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">else</span> &#123;</span><br><span class="line">					<span class="comment">//没有使用Spring默认的XML命名空间，则使用用户自定义的解//析规则解析元素节点</span></span><br><span class="line">					delegate.parseCustomElement(ele);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		<span class="comment">//Document的根节点没有使用Spring默认的命名空间，则使用用户自定义的</span></span><br><span class="line">		<span class="comment">//解析规则解析Document根节点</span></span><br><span class="line">		delegate.parseCustomElement(root);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用Spring的Bean规则解析Document元素节点</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">parseDefaultElement</span><span class="params">(Element ele, BeanDefinitionParserDelegate delegate)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//如果元素节点是&lt;Import&gt;导入元素，进行导入解析</span></span><br><span class="line">	<span class="keyword">if</span> (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) &#123;</span><br><span class="line">		importBeanDefinitionResource(ele);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果元素节点是&lt;Alias&gt;别名元素，进行别名解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) &#123;</span><br><span class="line">		processAliasRegistration(ele);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//元素节点既不是导入元素，也不是别名元素，即普通的&lt;Bean&gt;元素，</span></span><br><span class="line">	<span class="comment">//按照Spring的Bean规则解析元素</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) &#123;</span><br><span class="line">		processBeanDefinition(ele, delegate);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) &#123;</span><br><span class="line">		<span class="comment">// recurse</span></span><br><span class="line">		doRegisterBeanDefinitions(ele);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过上述SpringIOC容器对载入的Bean定义Document解析可以看出，我们使用Spring时，在Spring配置文件中可以使用<code>&lt;import&gt;</code>元素来导入IOC容器所需要的其他资源，SpringIOC容器在解析时会首先将指定导入的资源加载进容器中。使用<code>&lt;ailas&gt;</code>别名时，SpringIOC容器首先将别名元素所定义的别名注册到容器中。</p>
<p>对于既不是<code>&lt;import&gt;</code>元素，又不是<code>&lt;alias&gt;</code>元素的元素，即Spring配置文件中普通的<code>&lt;bean&gt;</code>元素的解析，由BeanDefinitionParserDelegate类的<code>parseBeanDefinitionElement()</code>方法来实现。</p>
<h3 id="载入-lt-bean-gt-元素"><a href="#载入-lt-bean-gt-元素" class="headerlink" title="载入&lt;bean&gt;元素"></a>载入<code>&lt;bean&gt;</code>元素</h3><p>Bean配置信息中的<code>&lt;import&gt;</code>和<code>&lt;alias&gt;</code>元素解析在DefaultBeanDefinitionDocumentReader中已经完成，对Bean配置信息中使用最多的<code>&lt;bean&gt;</code>元素交由BeanDefinitionParserDelegate来解析，其解析实现的源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br></pre></td><td class="code"><pre><span class="line">	<span class="comment">//解析&lt;Bean&gt;元素的入口</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> BeanDefinitionHolder <span class="title">parseBeanDefinitionElement</span><span class="params">(Element ele)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> parseBeanDefinitionElement(ele, <span class="keyword">null</span>);</span><br><span class="line">	&#125;	</span><br><span class="line"><span class="comment">//解析Bean定义资源文件中的&lt;Bean&gt;元素，这个方法中主要处理&lt;Bean&gt;元素的id，name和别名属性</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> BeanDefinitionHolder <span class="title">parseBeanDefinitionElement</span><span class="params">(Element ele, @Nullable BeanDefinition containingBean)</span> </span>&#123;</span><br><span class="line">		<span class="comment">//获取&lt;Bean&gt;元素中的id属性值</span></span><br><span class="line">		String id = ele.getAttribute(ID_ATTRIBUTE);</span><br><span class="line">		<span class="comment">//获取&lt;Bean&gt;元素中的name属性值</span></span><br><span class="line">		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);</span><br><span class="line"></span><br><span class="line">		<span class="comment">//获取&lt;Bean&gt;元素中的alias属性值</span></span><br><span class="line">		List&lt;String&gt; aliases = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">		<span class="comment">//将&lt;Bean&gt;元素中的所有name属性值存放到别名中</span></span><br><span class="line">		<span class="keyword">if</span> (StringUtils.hasLength(nameAttr)) &#123;</span><br><span class="line">			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);</span><br><span class="line">			aliases.addAll(Arrays.asList(nameArr));</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		String beanName = id;</span><br><span class="line">		<span class="comment">//如果&lt;Bean&gt;元素中没有配置id属性时，将别名中的第一个值赋值给beanName</span></span><br><span class="line">		<span class="keyword">if</span> (!StringUtils.hasText(beanName) &amp;&amp; !aliases.isEmpty()) &#123;</span><br><span class="line">			beanName = aliases.remove(<span class="number">0</span>);</span><br><span class="line">			<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">				logger.debug(<span class="string">"No XML 'id' specified - using '"</span> + beanName +</span><br><span class="line">						<span class="string">"' as bean name and "</span> + aliases + <span class="string">" as aliases"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//检查&lt;Bean&gt;元素所配置的id或者name的唯一性，containingBean标识&lt;Bean&gt;</span></span><br><span class="line">		<span class="comment">//元素中是否包含子&lt;Bean&gt;元素</span></span><br><span class="line">		<span class="keyword">if</span> (containingBean == <span class="keyword">null</span>) &#123;</span><br><span class="line">			<span class="comment">//检查&lt;Bean&gt;元素所配置的id、name或者别名是否重复</span></span><br><span class="line">			checkNameUniqueness(beanName, aliases, ele);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//详细对&lt;Bean&gt;元素中配置的Bean定义进行解析的地方</span></span><br><span class="line">		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);</span><br><span class="line">		<span class="keyword">if</span> (beanDefinition != <span class="keyword">null</span>) &#123;</span><br><span class="line">			<span class="keyword">if</span> (!StringUtils.hasText(beanName)) &#123;</span><br><span class="line">				<span class="keyword">try</span> &#123;</span><br><span class="line">					<span class="keyword">if</span> (containingBean != <span class="keyword">null</span>) &#123;</span><br><span class="line">						<span class="comment">//如果&lt;Bean&gt;元素中没有配置id、别名或者name，且没有包含子元素</span></span><br><span class="line">						<span class="comment">//&lt;Bean&gt;元素，为解析的Bean生成一个唯一beanName并注册</span></span><br><span class="line">						beanName = BeanDefinitionReaderUtils.generateBeanName(</span><br><span class="line">								beanDefinition, <span class="keyword">this</span>.readerContext.getRegistry(), <span class="keyword">true</span>);</span><br><span class="line">					&#125;</span><br><span class="line">					<span class="keyword">else</span> &#123;</span><br><span class="line">						<span class="comment">//如果&lt;Bean&gt;元素中没有配置id、别名或者name，且包含了子元素</span></span><br><span class="line">						<span class="comment">//&lt;Bean&gt;元素，为解析的Bean使用别名向IOC容器注册</span></span><br><span class="line">						beanName = <span class="keyword">this</span>.readerContext.generateBeanName(beanDefinition);</span><br><span class="line">						<span class="comment">// Register an alias for the plain bean class name, if still possible,</span></span><br><span class="line">						<span class="comment">// if the generator returned the class name plus a suffix.</span></span><br><span class="line">						<span class="comment">// This is expected for Spring 1.2/2.0 backwards compatibility.</span></span><br><span class="line">						<span class="comment">//为解析的Bean使用别名注册时，为了向后兼容</span></span><br><span class="line">						<span class="comment">//Spring1.2/2.0，给别名添加类名后缀</span></span><br><span class="line">						String beanClassName = beanDefinition.getBeanClassName();</span><br><span class="line">						<span class="keyword">if</span> (beanClassName != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">								beanName.startsWith(beanClassName) &amp;&amp; beanName.length() &gt; beanClassName.length() &amp;&amp;</span><br><span class="line">								!<span class="keyword">this</span>.readerContext.getRegistry().isBeanNameInUse(beanClassName)) &#123;</span><br><span class="line">							aliases.add(beanClassName);</span><br><span class="line">						&#125;</span><br><span class="line">					&#125;</span><br><span class="line">					<span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">						logger.debug(<span class="string">"Neither XML 'id' nor 'name' specified - "</span> +</span><br><span class="line">								<span class="string">"using generated bean name ["</span> + beanName + <span class="string">"]"</span>);</span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line">				<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">					error(ex.getMessage(), ele);</span><br><span class="line">					<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">			String[] aliasesArray = StringUtils.toStringArray(aliases);</span><br><span class="line">			<span class="keyword">return</span> <span class="keyword">new</span> BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//当解析出错时，返回null</span></span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//详细对&lt;Bean&gt;元素中配置的Bean定义其他属性进行解析</span></span><br><span class="line">	<span class="comment">//由于上面的方法中已经对Bean的id、name和别名等属性进行了处理</span></span><br><span class="line">	<span class="comment">//该方法中主要处理除这三个以外的其他属性数据</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> AbstractBeanDefinition <span class="title">parseBeanDefinitionElement</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">			Element ele, String beanName, @Nullable BeanDefinition containingBean)</span> </span>&#123;</span><br><span class="line">		<span class="comment">//记录解析的&lt;Bean&gt;</span></span><br><span class="line">		<span class="keyword">this</span>.parseState.push(<span class="keyword">new</span> BeanEntry(beanName));</span><br><span class="line"></span><br><span class="line">		<span class="comment">//这里只读取&lt;Bean&gt;元素中配置的class名字，然后载入到BeanDefinition中去</span></span><br><span class="line">		<span class="comment">//只是记录配置的class名字，不做实例化，对象的实例化在依赖注入时完成</span></span><br><span class="line">		String className = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//如果&lt;Bean&gt;元素中配置了parent属性，则获取parent属性的值</span></span><br><span class="line">		<span class="keyword">if</span> (ele.hasAttribute(CLASS_ATTRIBUTE)) &#123;</span><br><span class="line">			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();</span><br><span class="line">		&#125;</span><br><span class="line">		String parent = <span class="keyword">null</span>;</span><br><span class="line">		<span class="keyword">if</span> (ele.hasAttribute(PARENT_ATTRIBUTE)) &#123;</span><br><span class="line">			parent = ele.getAttribute(PARENT_ATTRIBUTE);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">//根据&lt;Bean&gt;元素配置的class名称和parent属性值创建BeanDefinition</span></span><br><span class="line">			<span class="comment">//为载入Bean定义信息做准备</span></span><br><span class="line">			AbstractBeanDefinition bd = createBeanDefinition(className, parent);</span><br><span class="line"></span><br><span class="line">			<span class="comment">//对当前的&lt;Bean&gt;元素中配置的一些属性进行解析和设置，如配置的单态(singleton)属性等</span></span><br><span class="line">			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);</span><br><span class="line">			<span class="comment">//为&lt;Bean&gt;元素解析的Bean设置description信息</span></span><br><span class="line">			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));</span><br><span class="line"></span><br><span class="line">			<span class="comment">//对&lt;Bean&gt;元素的meta(元信息)属性解析</span></span><br><span class="line">			parseMetaElements(ele, bd);</span><br><span class="line">			<span class="comment">//对&lt;Bean&gt;元素的lookup-method属性解析</span></span><br><span class="line">			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());</span><br><span class="line">			<span class="comment">//对&lt;Bean&gt;元素的replaced-method属性解析</span></span><br><span class="line">			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());</span><br><span class="line"></span><br><span class="line">			<span class="comment">//解析&lt;Bean&gt;元素的构造方法设置</span></span><br><span class="line">			parseConstructorArgElements(ele, bd);</span><br><span class="line">			<span class="comment">//解析&lt;Bean&gt;元素的&lt;property&gt;设置</span></span><br><span class="line">			parsePropertyElements(ele, bd);</span><br><span class="line">			<span class="comment">//解析&lt;Bean&gt;元素的qualifier属性</span></span><br><span class="line">			parseQualifierElements(ele, bd);</span><br><span class="line"></span><br><span class="line">			<span class="comment">//为当前解析的Bean设置所需的资源和依赖对象</span></span><br><span class="line">			bd.setResource(<span class="keyword">this</span>.readerContext.getResource());</span><br><span class="line">			bd.setSource(extractSource(ele));</span><br><span class="line"></span><br><span class="line">			<span class="keyword">return</span> bd;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (ClassNotFoundException ex) &#123;</span><br><span class="line">			error(<span class="string">"Bean class ["</span> + className + <span class="string">"] not found"</span>, ele, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (NoClassDefFoundError err) &#123;</span><br><span class="line">			error(<span class="string">"Class that bean class ["</span> + className + <span class="string">"] depends on not found"</span>, ele, err);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">			error(<span class="string">"Unexpected failure during bean definition parsing"</span>, ele, ex);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			<span class="keyword">this</span>.parseState.pop();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">//解析&lt;Bean&gt;元素出错时，返回null</span></span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="function"><span class="keyword">protected</span> AbstractBeanDefinition <span class="title">createBeanDefinition</span><span class="params">(@Nullable String className, @Nullable String parentName)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> ClassNotFoundException </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> BeanDefinitionReaderUtils.createBeanDefinition(</span><br><span class="line">				parentName, className, <span class="keyword">this</span>.readerContext.getBeanClassLoader());</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>只要使用过Spring，对Spring配置文件比较熟悉的人通过对上述源码的分析就会明白我们在Spring配置文件中<code>&lt;Bean&gt;</code>元素的中配置的属性就是通过该方法解析和设置到Bean中去的。</p>
<p>注意：在解析<code>&lt;Bean&gt;</code>元素过程中没有创建和实例化Bean对象，只是创建了Bean对象的定义类BeanDefinition，将<code>&lt;Bean&gt;</code>元素中的配置信息设置到BeanDefinition中作为记录，当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。</p>
<p>上面方法中一些对一些配置如元信息（meta）qualifier等的解析，我们在Spring中配置时使用的也不多，我们在使用Spring的<code>&lt;Bean&gt;</code>元素时，配置最多的是<code>&lt;property&gt;</code>属性，因此我们下面继续分析源码，了解Bean的属性在解析时是如何设置的。</p>
<h3 id="载入-lt-property-gt-元素"><a href="#载入-lt-property-gt-元素" class="headerlink" title="载入&lt;property&gt;元素"></a>载入<code>&lt;property&gt;</code>元素</h3><p>BeanDefinitionParserDelegate在解析<code>&lt;Bean&gt;</code>调用parsePropertyElements（方法解析<code>&lt;Bean&gt;</code>元素中的<code>&lt;property&gt;</code>属性子元素，解析源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//解析&lt;Bean&gt;元素中的&lt;property&gt;子元素</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">parsePropertyElements</span><span class="params">(Element beanEle, BeanDefinition bd)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//获取&lt;Bean&gt;元素中所有的子元素</span></span><br><span class="line">	NodeList nl = beanEle.getChildNodes();</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nl.getLength(); i++) &#123;</span><br><span class="line">		Node node = nl.item(i);</span><br><span class="line">		<span class="comment">//如果子元素是&lt;property&gt;子元素，则调用解析&lt;property&gt;子元素方法解析</span></span><br><span class="line">		<span class="keyword">if</span> (isCandidateElement(node) &amp;&amp; nodeNameEquals(node, PROPERTY_ELEMENT)) &#123;</span><br><span class="line">			parsePropertyElement((Element) node, bd);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//解析&lt;property&gt;元素</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">parsePropertyElement</span><span class="params">(Element ele, BeanDefinition bd)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//获取&lt;property&gt;元素的名字</span></span><br><span class="line">	String propertyName = ele.getAttribute(NAME_ATTRIBUTE);</span><br><span class="line">	<span class="keyword">if</span> (!StringUtils.hasLength(propertyName)) &#123;</span><br><span class="line">		error(<span class="string">"Tag 'property' must have a 'name' attribute"</span>, ele);</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">this</span>.parseState.push(<span class="keyword">new</span> PropertyEntry(propertyName));</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		<span class="comment">//如果一个Bean中已经有同名的property存在，则不进行解析，直接返回。</span></span><br><span class="line">		<span class="comment">//即如果在同一个Bean中配置同名的property，则只有第一个起作用</span></span><br><span class="line">		<span class="keyword">if</span> (bd.getPropertyValues().contains(propertyName)) &#123;</span><br><span class="line">			error(<span class="string">"Multiple 'property' definitions for property '"</span> + propertyName + <span class="string">"'"</span>, ele);</span><br><span class="line">			<span class="keyword">return</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//解析获取property的值</span></span><br><span class="line">		Object val = parsePropertyValue(ele, bd, propertyName);</span><br><span class="line">		<span class="comment">//根据property的名字和值创建property实例</span></span><br><span class="line">		PropertyValue pv = <span class="keyword">new</span> PropertyValue(propertyName, val);</span><br><span class="line">		<span class="comment">//解析&lt;property&gt;元素中的属性</span></span><br><span class="line">		parseMetaElements(ele, pv);</span><br><span class="line">		pv.setSource(extractSource(ele));</span><br><span class="line">		bd.getPropertyValues().addPropertyValue(pv);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">finally</span> &#123;</span><br><span class="line">		<span class="keyword">this</span>.parseState.pop();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过对上述源码的分析，我们可以了解在Spring配置文件中，<code>&lt;Bean&gt;</code>元素中<code>&lt;property&gt;</code>元素的相关配置是如何处理的：</p>
<p>1、ref被封装为指向依赖对象一个引用。</p>
<p>2、value配置都会封装成一个字符串类型的对象。</p>
<p>3、ref和value都通过”解析的数据类型属性值.setSource(extractSource(ele))”，方法将属性值/引用与所引用的属性关联起来。</p>
<p>在方法的最后对于<code>&lt;property&gt;</code>元素的子元素通过<code>parsePropertySubElement()</code>方法解析，我们继续分析该方法的源码，了解其解析过程。</p>
<h3 id="载入-lt-property-gt-的子元素"><a href="#载入-lt-property-gt-的子元素" class="headerlink" title="载入&lt;property&gt;的子元素"></a>载入<code>&lt;property&gt;</code>的子元素</h3><p>在BeanDefinitionParserDelegate类中的parsePropertySubElement（方法对<code>&lt;property&gt;</code>中的子元素解析，源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">parsePropertySubElement</span><span class="params">(Element ele, @Nullable BeanDefinition bd)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">return</span> parsePropertySubElement(ele, bd, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Parse a value, ref or collection sub-element of a property or</span></span><br><span class="line"><span class="comment"> * constructor-arg element.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ele subelement of property element; we don't know which yet</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> defaultValueType the default type (class name) for any</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> &lt;value&gt;&#125; tag that might be created</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//解析&lt;property&gt;元素中ref,value或者集合等子元素</span></span><br><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">parsePropertySubElement</span><span class="params">(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//如果&lt;property&gt;没有使用Spring默认的命名空间，则使用用户自定义的规则解析内嵌元素</span></span><br><span class="line">	<span class="keyword">if</span> (!isDefaultNamespace(ele)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseNestedCustomElement(ele, bd);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是bean，则使用解析&lt;Bean&gt;元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, BEAN_ELEMENT)) &#123;</span><br><span class="line">		BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);</span><br><span class="line">		<span class="keyword">if</span> (nestedBd != <span class="keyword">null</span>) &#123;</span><br><span class="line">			nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> nestedBd;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是ref，ref中只能有以下3个属性：bean、local、parent</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, REF_ELEMENT)) &#123;</span><br><span class="line">		<span class="comment">// A generic reference to any name of any bean.</span></span><br><span class="line">		<span class="comment">//可以不再同一个Spring配置文件中，具体请参考Spring对ref的配置规则</span></span><br><span class="line">		String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);</span><br><span class="line">		<span class="keyword">boolean</span> toParent = <span class="keyword">false</span>;</span><br><span class="line">		<span class="keyword">if</span> (!StringUtils.hasLength(refName)) &#123;</span><br><span class="line">			<span class="comment">// A reference to the id of another bean in a parent context.</span></span><br><span class="line">			<span class="comment">//获取&lt;property&gt;元素中parent属性值，引用父级容器中的Bean</span></span><br><span class="line">			refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);</span><br><span class="line">			toParent = <span class="keyword">true</span>;</span><br><span class="line">			<span class="keyword">if</span> (!StringUtils.hasLength(refName)) &#123;</span><br><span class="line">				error(<span class="string">"'bean' or 'parent' is required for &lt;ref&gt; element"</span>, ele);</span><br><span class="line">				<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (!StringUtils.hasText(refName)) &#123;</span><br><span class="line">			error(<span class="string">"&lt;ref&gt; element contains empty target attribute"</span>, ele);</span><br><span class="line">			<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//创建ref类型数据，指向被引用的对象</span></span><br><span class="line">		RuntimeBeanReference ref = <span class="keyword">new</span> RuntimeBeanReference(refName, toParent);</span><br><span class="line">		<span class="comment">//设置引用类型值是被当前子元素所引用</span></span><br><span class="line">		ref.setSource(extractSource(ele));</span><br><span class="line">		<span class="keyword">return</span> ref;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;idref&gt;，使用解析ref元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, IDREF_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseIdRefElement(ele);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;value&gt;，使用解析value元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, VALUE_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseValueElement(ele, defaultValueType);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是null，为&lt;property&gt;设置一个封装null值的字符串数据</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, NULL_ELEMENT)) &#123;</span><br><span class="line">		<span class="comment">// It's a distinguished null value. Let's wrap it in a TypedStringValue</span></span><br><span class="line">		<span class="comment">// object in order to preserve the source location.</span></span><br><span class="line">		TypedStringValue nullHolder = <span class="keyword">new</span> TypedStringValue(<span class="keyword">null</span>);</span><br><span class="line">		nullHolder.setSource(extractSource(ele));</span><br><span class="line">		<span class="keyword">return</span> nullHolder;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;array&gt;，使用解析array集合子元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, ARRAY_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseArrayElement(ele, bd);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;list&gt;，使用解析list集合子元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, LIST_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseListElement(ele, bd);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;set&gt;，使用解析set集合子元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, SET_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseSetElement(ele, bd);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;map&gt;，使用解析map集合子元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, MAP_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parseMapElement(ele, bd);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//如果子元素是&lt;props&gt;，使用解析props集合子元素的方法解析</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (nodeNameEquals(ele, PROPS_ELEMENT)) &#123;</span><br><span class="line">		<span class="keyword">return</span> parsePropsElement(ele);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//既不是ref，又不是value，也不是集合，则子元素配置错误，返回null</span></span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		error(<span class="string">"Unknown property sub-element: ["</span> + ele.getNodeName() + <span class="string">"]"</span>, ele);</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过上述源码分析，我们明白了在Spring配置文件中，对<code>&lt;property&gt;</code>元素中配置的array、list、set、map、prop等各种集合子元素的都通过上述方法解析，生成对应的数据对象，比如ManagedList、ManagedArray、ManagedSet等，这些Managed类是Spring对象BeanDefiniton的数据封装，对集合数据类型的解析都有各自的方法实现，解析方法的命名非常规范，一目了然，我们对<code>&lt;list&gt;</code>集合元素的解析方法进行源码分析，了解其实现过程。</p>
<h3 id="载入-lt-list-gt-的子元素"><a href="#载入-lt-list-gt-的子元素" class="headerlink" title="载入&lt;list&gt;的子元素"></a>载入<code>&lt;list&gt;</code>的子元素</h3><p>在BeanDefinitionParserDelegate类中的<code>parselistElement()</code>方法就是具体实现解析<code>&lt;property&gt;</code>元素中的<code>&lt;list&gt;</code>集合子元素，源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//解析&lt;list&gt;集合子元素</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> List&lt;Object&gt; <span class="title">parseListElement</span><span class="params">(Element collectionEle, @Nullable BeanDefinition bd)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//获取&lt;list&gt;元素中的value-type属性，即获取集合元素的数据类型</span></span><br><span class="line">	String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);</span><br><span class="line">	<span class="comment">//获取&lt;list&gt;集合元素中的所有子节点</span></span><br><span class="line">	NodeList nl = collectionEle.getChildNodes();</span><br><span class="line">	<span class="comment">//Spring中将List封装为ManagedList</span></span><br><span class="line">	ManagedList&lt;Object&gt; target = <span class="keyword">new</span> ManagedList&lt;&gt;(nl.getLength());</span><br><span class="line">	target.setSource(extractSource(collectionEle));</span><br><span class="line">	<span class="comment">//设置集合目标数据类型</span></span><br><span class="line">	target.setElementTypeName(defaultElementType);</span><br><span class="line">	target.setMergeEnabled(parseMergeAttribute(collectionEle));</span><br><span class="line">	<span class="comment">//具体的&lt;list&gt;元素解析</span></span><br><span class="line">	parseCollectionElements(nl, target, bd, defaultElementType);</span><br><span class="line">	<span class="keyword">return</span> target;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//具体解析&lt;list&gt;集合元素，&lt;array&gt;、&lt;list&gt;和&lt;set&gt;都使用该方法解析</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">parseCollectionElements</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">		NodeList elementNodes, Collection&lt;Object&gt; target, @Nullable BeanDefinition bd, String defaultElementType)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//遍历集合所有节点</span></span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; elementNodes.getLength(); i++) &#123;</span><br><span class="line">		Node node = elementNodes.item(i);</span><br><span class="line">		<span class="comment">//节点不是description节点</span></span><br><span class="line">		<span class="keyword">if</span> (node <span class="keyword">instanceof</span> Element &amp;&amp; !nodeNameEquals(node, DESCRIPTION_ELEMENT)) &#123;</span><br><span class="line">			<span class="comment">//将解析的元素加入集合中，递归调用下一个子元素</span></span><br><span class="line">			target.add(parsePropertySubElement((Element) node, bd, defaultElementType));</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>经过对Spring Bean配置信息转换的Document对象中的元素层层解析SpringIOC现在已经将XML形式定义的Bean配置信息转换为SpringIOC所识别的数据结构BeanDefinition，它是Bean配置信息中配置的POJO对象在SpringIOC容器中的映射，我们可以通过AbstractBeanDefinition为入口，看到了IOC容器进行索引、查询和操作。</p>
<p>通过SpringIOC容器对Bean配置资源的解析后，IOC容器大致完成了管理Bean对象的准备工作，即初始化过程，但是最为重要的依赖注入还没有发生，现在在IOC容器中BeanDefinition存储的只是一些静态信息，接下来需要向容器注册Bean定义信息才能全部完成IOC容器的初始化过程。</p>
<h3 id="分配注册策略"><a href="#分配注册策略" class="headerlink" title="分配注册策略"></a>分配注册策略</h3><p>继续跟踪程序的执行顺序，接下来来分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中，在其<code>parseDefaultElement()</code>方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象，然后调用BeanDefinitionReaderUtils 的<code>registerBeanDefinition()</code>方法向IOC容器注册解析的Bean，BeanDefinitionReaderUtils的注册的源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//将解析的BeanDefinitionHold注册到容器中</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">registerBeanDefinition</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Register bean definition under primary name.</span></span><br><span class="line">	<span class="comment">//获取解析的BeanDefinition的名称</span></span><br><span class="line">	String beanName = definitionHolder.getBeanName();</span><br><span class="line">	<span class="comment">//向IOC容器注册BeanDefinition</span></span><br><span class="line">	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Register aliases for bean name, if any.</span></span><br><span class="line">	<span class="comment">//如果解析的BeanDefinition有别名，向容器为其注册别名</span></span><br><span class="line">	String[] aliases = definitionHolder.getAliases();</span><br><span class="line">	<span class="keyword">if</span> (aliases != <span class="keyword">null</span>) &#123;</span><br><span class="line">		<span class="keyword">for</span> (String alias : aliases) &#123;</span><br><span class="line">			registry.registerAlias(beanName, alias);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当调用BeanDefinitionReaderUtils向IOC容器注册解析的BeanDefinition时，真正完成注册功能的是DefaultListableBeanFactory。</p>
<h3 id="向容器注册"><a href="#向容器注册" class="headerlink" title="向容器注册"></a>向容器注册</h3><p>DefaultListableBeanFactory中使用一个HashMap的集合对象存放IOC容器中注册解析的BeanDefinition，向IOC容器注册的主要源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//向IOC容器注册解析的BeanDefiniton</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinition</span><span class="params">(String beanName, BeanDefinition beanDefinition)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> BeanDefinitionStoreException </span>&#123;</span><br><span class="line"></span><br><span class="line">	Assert.hasText(beanName, <span class="string">"Bean name must not be empty"</span>);</span><br><span class="line">	Assert.notNull(beanDefinition, <span class="string">"BeanDefinition must not be null"</span>);</span><br><span class="line"></span><br><span class="line">	<span class="comment">//校验解析的BeanDefiniton</span></span><br><span class="line">	<span class="keyword">if</span> (beanDefinition <span class="keyword">instanceof</span> AbstractBeanDefinition) &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			((AbstractBeanDefinition) beanDefinition).validate();</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (BeanDefinitionValidationException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,</span><br><span class="line">					<span class="string">"Validation of bean definition failed"</span>, ex);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	BeanDefinition oldBeanDefinition;</span><br><span class="line"></span><br><span class="line">	oldBeanDefinition = <span class="keyword">this</span>.beanDefinitionMap.get(beanName);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (oldBeanDefinition != <span class="keyword">null</span>) &#123;</span><br><span class="line">		<span class="keyword">if</span> (!isAllowBeanDefinitionOverriding()) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,</span><br><span class="line">					<span class="string">"Cannot register bean definition ["</span> + beanDefinition + <span class="string">"] for bean '"</span> + beanName +</span><br><span class="line">					<span class="string">"': There is already ["</span> + oldBeanDefinition + <span class="string">"] bound."</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (oldBeanDefinition.getRole() &lt; beanDefinition.getRole()) &#123;</span><br><span class="line">			<span class="comment">// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE</span></span><br><span class="line">			<span class="keyword">if</span> (<span class="keyword">this</span>.logger.isWarnEnabled()) &#123;</span><br><span class="line">				<span class="keyword">this</span>.logger.warn(<span class="string">"Overriding user-defined bean definition for bean '"</span> + beanName +</span><br><span class="line">						<span class="string">"' with a framework-generated bean definition: replacing ["</span> +</span><br><span class="line">						oldBeanDefinition + <span class="string">"] with ["</span> + beanDefinition + <span class="string">"]"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (!beanDefinition.equals(oldBeanDefinition)) &#123;</span><br><span class="line">			<span class="keyword">if</span> (<span class="keyword">this</span>.logger.isInfoEnabled()) &#123;</span><br><span class="line">				<span class="keyword">this</span>.logger.info(<span class="string">"Overriding bean definition for bean '"</span> + beanName +</span><br><span class="line">						<span class="string">"' with a different definition: replacing ["</span> + oldBeanDefinition +</span><br><span class="line">						<span class="string">"] with ["</span> + beanDefinition + <span class="string">"]"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (<span class="keyword">this</span>.logger.isDebugEnabled()) &#123;</span><br><span class="line">				<span class="keyword">this</span>.logger.debug(<span class="string">"Overriding bean definition for bean '"</span> + beanName +</span><br><span class="line">						<span class="string">"' with an equivalent definition: replacing ["</span> + oldBeanDefinition +</span><br><span class="line">						<span class="string">"] with ["</span> + beanDefinition + <span class="string">"]"</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">this</span>.beanDefinitionMap.put(beanName, beanDefinition);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		<span class="keyword">if</span> (hasBeanCreationStarted()) &#123;</span><br><span class="line">			<span class="comment">// Cannot modify startup-time collection elements anymore (for stable iteration)</span></span><br><span class="line">			<span class="comment">//注册的过程中需要线程同步，以保证数据的一致性</span></span><br><span class="line">			<span class="keyword">synchronized</span> (<span class="keyword">this</span>.beanDefinitionMap) &#123;</span><br><span class="line">				<span class="keyword">this</span>.beanDefinitionMap.put(beanName, beanDefinition);</span><br><span class="line">				List&lt;String&gt; updatedDefinitions = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="keyword">this</span>.beanDefinitionNames.size() + <span class="number">1</span>);</span><br><span class="line">				updatedDefinitions.addAll(<span class="keyword">this</span>.beanDefinitionNames);</span><br><span class="line">				updatedDefinitions.add(beanName);</span><br><span class="line">				<span class="keyword">this</span>.beanDefinitionNames = updatedDefinitions;</span><br><span class="line">				<span class="keyword">if</span> (<span class="keyword">this</span>.manualSingletonNames.contains(beanName)) &#123;</span><br><span class="line">					Set&lt;String&gt; updatedSingletons = <span class="keyword">new</span> LinkedHashSet&lt;&gt;(<span class="keyword">this</span>.manualSingletonNames);</span><br><span class="line">					updatedSingletons.remove(beanName);</span><br><span class="line">					<span class="keyword">this</span>.manualSingletonNames = updatedSingletons;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> &#123;</span><br><span class="line">			<span class="comment">// Still in startup registration phase</span></span><br><span class="line">			<span class="keyword">this</span>.beanDefinitionMap.put(beanName, beanDefinition);</span><br><span class="line">			<span class="keyword">this</span>.beanDefinitionNames.add(beanName);</span><br><span class="line">			<span class="keyword">this</span>.manualSingletonNames.remove(beanName);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">this</span>.frozenBeanDefinitionNames = <span class="keyword">null</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//检查是否有同名的BeanDefinition已经在IOC容器中注册</span></span><br><span class="line">	<span class="keyword">if</span> (oldBeanDefinition != <span class="keyword">null</span> || containsSingleton(beanName)) &#123;</span><br><span class="line">		<span class="comment">//重置所有已经注册过的BeanDefinition的缓存</span></span><br><span class="line">		resetBeanDefinition(beanName);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，Bean配置信息中配置的Bean被解析过后，已经注册到IOC容器中，被容器管理起来，真正完成了IOC容器初始化所做的全部工作。现在I0C容器中已经建立了整个Bean的配置信息，这些BeanDefinition信息已经可以使用，并且可以被检索，IOC容器的作用就是对这些注册的Bean定义信息进行处理和维护。</p>
<p>这些的注册的Bean定义信息是IOC容器控制反转的基础，正是有了这些注册的数据，容器才可以进行依赖注入。</p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://dxysun.com/2021/08/29/springForMainClass/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spring/" rel="tag">spring</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2021/08/30/springForIOCAnnotation/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Spring核心之基于Annotation的IOC容器初始化
          
        </div>
      </a>
    
    
      <a href="/2021/08/29/springForImport/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Spring中@Import注解的使用和实现原理</div>
      </a>
    
  </nav>

  
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2024
        <i class="ri-heart-fill heart_icon"></i> dxysun
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
        <li>
          <a href="https://beian.miit.gov.cn" target="_black" rel="nofollow">豫ICP备17012675号-1</a>
        </li>
        
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="https://dxysun.com/static/logo.png" alt="迎着朝阳"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">相册</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/alipay-20201219151322.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/weixin-20201219151346.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>